package com.moxi.mogublog.redis.config;

import com.moxi.mogublog.redis.properties.RedisProperties;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Jedis 线程池配置
 *
 * @author Lishuai
 * @version 1.0.0
 * @date 2020-04-21
 */
@Configuration
@EnableCaching
public class RedisPoolFactory extends CachingConfigurerSupport {

    private final static Logger log = LoggerFactory.getLogger(RedisPoolFactory.class);

    @Autowired
    private RedisProperties redisProperties;

    /**
     * redisPoolFactory Redis线程池工厂
     *
     * @return
     */
    @Bean(name = "jedisPoolFactory")
    public JedisPool redisPoolFactory() {
        try {
            JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
            jedisPoolConfig.setBlockWhenExhausted(redisProperties.isBlockWhenExhausted());
            jedisPoolConfig.setEvictionPolicyClassName(redisProperties.getEvictionPolicyClassname());
            jedisPoolConfig.setJmxEnabled(redisProperties.isJmxEnabled());
            jedisPoolConfig.setJmxNamePrefix(redisProperties.getJmxNamePrefix());
            jedisPoolConfig.setLifo(redisProperties.isLifo());
            jedisPoolConfig.setMaxIdle(redisProperties.getMaxIdle());
            jedisPoolConfig.setMaxTotal(redisProperties.getMaxTotal());
            jedisPoolConfig.setMaxWaitMillis(redisProperties.getMaxWaitMillis());
            jedisPoolConfig.setMinEvictableIdleTimeMillis(redisProperties.getMinEvictableIdleTimeMillis());
            jedisPoolConfig.setMinIdle(redisProperties.getMinIdle());
            jedisPoolConfig.setNumTestsPerEvictionRun(redisProperties.getNumTestsPerEvictionRun());
            jedisPoolConfig.setSoftMinEvictableIdleTimeMillis(redisProperties.getSoftMinEvictableIdleTimeMillis());
            jedisPoolConfig.setTestOnBorrow(redisProperties.isTestOnBorrow());
            jedisPoolConfig.setTestWhileIdle(redisProperties.isTestWhileIdle());
            jedisPoolConfig.setTimeBetweenEvictionRunsMillis(redisProperties.getTimeBetweenEvictionRunsMillis());

            if (StringUtils.isEmpty(redisProperties.getPassword())) {
                return new JedisPool(jedisPoolConfig, redisProperties.getHost(), redisProperties.getPort(), redisProperties.getTimeout(), null, redisProperties.getDatabase());
            } else {
                return new JedisPool(jedisPoolConfig, redisProperties.getHost(), redisProperties.getPort(), redisProperties.getTimeout(), redisProperties.getPassword(), redisProperties.getDatabase());
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
            return null;
        }

    }

    /**
     * 释放jedis资源
     *
     * @param jedis
     */
    public static void close(final Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }

//    @SuppressWarnings(value = {"unchecked", "rawtypes"})
//    @Bean(name = "redisTemplate")
//    @ConditionalOnMissingBean(name = "redisTemplate")
//    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
//        log.info("RedisPoolFactory.redisTemplate");
//        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
//        redisTemplate.setConnectionFactory(redisConnectionFactory);
//
//        // 序列化：使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
//        FastJson2JsonRedisSerializer fastJson2JsonRedisSerializer = new FastJson2JsonRedisSerializer(Object.class);
//
//        ObjectMapper mapper = new ObjectMapper();
//        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//        fastJson2JsonRedisSerializer.setObjectMapper(mapper);
//
//        // 设置值（value）的序列化采用 FastJson2JsonRedisSerializer
//        redisTemplate.setValueSerializer(fastJson2JsonRedisSerializer);
//        redisTemplate.setHashValueSerializer(fastJson2JsonRedisSerializer);
//        // 使用StringRedisSerializer来序列化和反序列化redis的key值
//        redisTemplate.setKeySerializer(new StringRedisSerializer());
//        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//        // 必须执行这个函数,初始化RedisTemplate
//        redisTemplate.afterPropertiesSet();
//        return redisTemplate;
//    }

    /**
     * 缓存管理器
     *
     * @param redisConnectionFactory
     * @return
     */
//    @Bean
//    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
//        RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
//                .RedisCacheManagerBuilder
//                .fromConnectionFactory(redisConnectionFactory);
//        return builder.build();
//    }

    /**
     * 自定义缓存key生成策略
     * 使用方法 @Cacheable(keyGenerator="keyGenerator")
     *
     * @return
     */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(method.getName());
            for (Object obj : params) {
                sb.append(obj.toString());
            }
            log.info(sb.toString());
            return sb.toString();
        };
    }

    /**
     * 处理错误的 Handler
     *
     * @return
     */
    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        // 异常处理，当Redis发生异常时，打印日志，但是程序正常走
        log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
        CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
                log.error("Redis occur handleCacheGetError：key -> [{}]", key, e);
            }

            @Override
            public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
                log.error("Redis occur handleCachePutError：key -> [{}]；value -> [{}]", key, value, e);
            }

            @Override
            public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
                log.error("Redis occur handleCacheEvictError：key -> [{}]", key, e);
            }

            @Override
            public void handleCacheClearError(RuntimeException e, Cache cache) {
                log.error("Redis occur handleCacheClearError：", e);
            }
        };
        return cacheErrorHandler;
    }

}
